package com.hzw.code.net

import android.util.Log.VERBOSE
import com.hzw.code.annotation.BaseUrl
import com.hzw.code.annotation.NoEncrypt
import com.hzw.code.annotation.NoToken
import com.hzw.code.net.bean.Http
import com.hzw.code.net.json.GsonClient
import com.ihsanbal.logging.Level
import com.safframework.log.L
import com.safframework.log.okhttp.LoggingInterceptor
import okhttp3.FormBody
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.POST
import java.util.concurrent.TimeUnit

/**
 *
 * @Author:         HuaZhongWei
 * @CreateDate:     2020/4/15 14:28
 *
 * @Description:    Http构建器
 *
 */
class HttpCreator private constructor() {

    companion object {
        fun create(
            clazz: Class<*>,
            isDebug: Boolean = true,
            readTimeout: Long = DEFAULT_HTTP_TIMEOUT,
            writeTimeout: Long = DEFAULT_HTTP_TIMEOUT,
            connectTimeout: Long = DEFAULT_HTTP_TIMEOUT,
            headers: Array<Pair<String, String>>,
            encrypt: ((String) -> String)? = null
        ) = Builder(clazz)
            .isDebug(isDebug)
            .readTimeout(readTimeout)
            .writeTimeout(writeTimeout)
            .connectTimeout(connectTimeout)
            .addHeaders(*headers)
            .encrypt(encrypt)
            .build()

        /**
         * 解析 API类的BaseUrl
         */
        fun parseApiBaseUrl(clazz: Class<*>): String {
            val baseUrlAnnotation = clazz.getAnnotation(BaseUrl::class.java)
            if (baseUrlAnnotation == null || baseUrlAnnotation.url.isEmpty()) {
                throw NullPointerException("Api Class 必须有 @BaseUrl(url = \"http://.... \") 注解")
            }
            return baseUrlAnnotation.url
        }

        /**
         * 解析Api文件
         */
        fun parseApiClass(clazz: Class<*>, http: Http) {
            http.baseUrl = parseApiBaseUrl(clazz)

            val methods = clazz.declaredMethods
            methods.forEach { method ->
                val noToken = method.getAnnotation(NoToken::class.java)
                val noEncrypt = method.getAnnotation(NoEncrypt::class.java)
                val post = method.getAnnotation(POST::class.java)
                val get = method.getAnnotation(GET::class.java)

                if (noToken != null) {
                    post?.let { http.noTokenSet.add(it.value) }
                    get?.let { http.noTokenSet.add(it.value) }
                }

                // get不考虑加密，只有post才考虑加密
                if (noEncrypt != null && post != null) {
                    http.noEncryptSet.add(post.value)
                }
            }
        }
    }

    private fun createRetrofit(builder: Builder, http: Http) {

        val clientBuilder = OkHttpClient.Builder()
            .connectTimeout(builder.connectTimeout, TimeUnit.MILLISECONDS)
            .readTimeout(builder.readTimeout, TimeUnit.MILLISECONDS)
            .writeTimeout(builder.writeTimeout, TimeUnit.MILLISECONDS)
            //添加日志连接
//            .addNetworkInterceptor(HttpLoggingInterceptor().setLevel(if (builder.isDebug) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE))

        val loggingInterceptor = LoggingInterceptor.Builder()
            .loggable(builder.isDebug)
            .request()
            .requestTag("请求体：")
            .response()
            .responseTag("返回体：")
            .build()

        clientBuilder.addNetworkInterceptor(loggingInterceptor)

        builder.interceptors.forEach { clientBuilder.addInterceptor(it) }

        if (builder.headers.isNotEmpty()) {
            //处理公共header的拦截器
            clientBuilder.addInterceptor { chain ->
                var request = chain.request()
                val noTokenSet = http.noTokenSet

                if (!builder.headers.isNullOrEmpty()) {
                    val newBuilder = request.headers.newBuilder()
                    builder.headers.forEach { header ->
                        if (header.first == HEADER_TOKEN) {
                            if (!isContains(noTokenSet, http.baseUrl!!, request.url.toString())) {
                                newBuilder.add(header.first, header.second)
                            }
                        } else {
                            newBuilder.add(header.first, header.second)
                        }
                    }
                    request = request.newBuilder().headers(newBuilder.build()).build()
                }
                chain.proceed(request)
            }
        }

        if (builder.encrypt != null) {
            // 处理加密的拦截器
            clientBuilder.addInterceptor { chain ->
                var request = chain.request()
                if (request.method == "POST" && request.body != null && !(request.body is FormBody) && !isContains(
                        http.noEncryptSet,
                        http.baseUrl!!,
                        request.url.toString()
                    )
                ) {
                    val buffer = Buffer()
                    request.body?.writeTo(buffer)
                    val oldData = buffer.readUtf8()
                    if (builder.isDebug) {
                        L.json(oldData)
                    }
                    val newData = builder.encrypt!!(oldData)
                    if (builder.isDebug) {
                        L.d("加密后 : $newData")
                    }
                    val requestBody = newData.toRequestBody(request.body?.contentType())
                    request = request.newBuilder().method(request.method, requestBody).build()
                }
                chain.proceed(request)
            }
        }

        val retrofit = Retrofit.Builder()
            .baseUrl(http.baseUrl!!)
            .client(clientBuilder.build())
            .addConverterFactory(GsonConverterFactory.create(GsonClient.create()))
            .build()

        http.retrofit = retrofit
    }

    /**
     * 判断集合中是否存在指定url
     * 集合中的url需要和baseUrl拼接成完整的url
     */
    private fun isContains(set: Set<String>, baseUrl: String, url: String): Boolean {
        if (set.isEmpty()) {
            return false
        }
        set.forEach {
            if (baseUrl + it == url) {
                return true
            }
        }
        return false
    }

    class Builder(private val clazz: Class<*>) {
        internal var isDebug: Boolean = true
        internal var readTimeout = DEFAULT_HTTP_TIMEOUT
        internal var writeTimeout = DEFAULT_HTTP_TIMEOUT
        internal var connectTimeout = DEFAULT_HTTP_TIMEOUT
        internal var interceptors = mutableListOf<Interceptor>()
        internal var headers = mutableListOf<Pair<String, String>>()
        internal var encrypt: ((String) -> String)? = null

        fun isDebug(isDebug: Boolean = true) = apply{
            this.isDebug = isDebug
            return this
        }

        fun readTimeout(timeout: Long = DEFAULT_HTTP_TIMEOUT) = apply{
            this.readTimeout = timeout
        }

        fun writeTimeout(timeout: Long = DEFAULT_HTTP_TIMEOUT) = apply{
            this.writeTimeout = timeout
        }

        fun connectTimeout(timeout: Long = DEFAULT_HTTP_TIMEOUT) = apply{
            this.connectTimeout = timeout
        }

        fun addInterceptor(vararg interceptors: Interceptor) = apply{
            this.interceptors.addAll(interceptors)
        }

        fun addHeaders(vararg headers: Pair<String, String>) = apply{
            this.headers.addAll(headers.toList())
        }

        fun encrypt(block: ((oldData: String) -> String)? = null) = apply{
            this.encrypt = block
        }

        fun build(): Http {
            val http = Http()
            val httpCreator = HttpCreator()
            parseApiClass(clazz, http)
            httpCreator.createRetrofit(this, http)
            HttpManager.addHttp(http.baseUrl!!, http)
            return http
        }
    }
}